package com.esdk.sql.orm;

import com.alibaba.fastjson.JSONArray;
import com.esdk.esdk;
import com.esdk.exception.SdkRuntimeException;
import com.esdk.interfaces.Func;
import com.esdk.sql.RowExpression;
import com.esdk.sql.RowExpressions;
import com.esdk.sql.SQLRuntimeException;
import com.esdk.utils.Constant;
import com.esdk.utils.EasyObj;
import lombok.NonNull;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.Predicate;
/***
 * @author 范飞宇
 * @since 2006.?.?
 * 用于可修改的数据集
 */
public class ABRowSet<T extends IRow> extends ARowSet<T> implements IRowSet<T>,Cloneable{
	public ABRowSet(){
		this(16);
	}
	public ABRowSet(Class<T> rowCls){
		super(rowCls);
	}

	public ABRowSet(IReadRowSet<T> rs){
		this(rs,true);
	}

	public ABRowSet(IReadRowSet<T> rs,boolean isLoadRows){
		this();
		if(rs instanceof ARowSet<?> ars){
			this._rowCls=(Class<T>)ars._rowCls;
			this.primaryKeyName=ars.primaryKeyName;
			this.columns=ars.columns;
			_abrs=ars._abrs;
		}else{
			setColumns(rs.getColumns());
		}
		if(isLoadRows)
			this.load(rs);
	}

	public ABRowSet(int initialCapacity){
		rowList=new ArrayList(initialCapacity);
		cursor=-1;
	}

	public ABRowSet(String...columns){
		super(columns);
	}

	public ABRowSet(String[][] array){
		super(array);
	}

	public ABRowSet(Object[][] array){
		this();
		columns=Arrays.asList(array[0]).toArray(new String[0]);
		String[] cols=esdk.str.toArray(columns);
		for(int i=1;i<array.length;i++){
			add((T)new ABRow(cols,array[i]));
		}
	}

	public ABRowSet(JSONArray jsarray){
		super(jsarray);
	}

	public  <R extends ParentRow>ABRowSet (JSONArray jsarray,Class<R> parentRowCls){
		super(jsarray,parentRowCls);
	}

	public  <R extends ParentRow>ABRowSet (List beanList,Class<R> parentRowCls){
		super(beanList,parentRowCls);
	}

	public ABRowSet(Collection<Map<String,?>> collMap){
		super(collMap);
	}

	public ABRowSet(File csvFile) throws IOException{
		super(csvFile);
	}

	public ABRowSet(InputStream is){
		super(esdk.str.fromCsv(esdk.str.isToStr(is)));
	}

	public ABRowSet(File csvFile,String charset) throws IOException{
		super(csvFile,charset);
	}

	public ABRowSet(File csvFile,String charset,boolean forceText) throws IOException{
		super(csvFile,charset,forceText);
	}

	public ABRowSet(ABResultSet rs){
		super(rs);
	}

	public ABRowSet(T[] rows){
		super(rows);
	}

	public ABRowSet(IResultSet rs,List<T> list){
		super(rs,list);
	}

	public ABRowSet(List<T> list){
		super(list);
	}

	public ABRowSet load(String jsonArrStr){
		this.removeAll();
		return this.load(esdk.json.toJsonArray(jsonArrStr));
	}

	public ABRowSet load(String jsonArrStr,Class<T> parentRowCls){
		this.removeAll();
		setParentRowCls(parentRowCls);
		return super.load(esdk.json.toJsonArray(jsonArrStr));
	}

	public ABRowSet load(IReadRowSet rs){
		this.removeAll();
		return this.addAll(rs);
	}

	public ABRowSet<T> load(List<T> list){
		rowList.addAll(list);
		return this;
	}

	/**
	 * 创建一条新记录添加到数据集尾部并返回
	 * */
	public T append(){
		T result=createRow();
		addLast(result);
		last();
		return result;
	}

	/**
	 * @param pos 在指定位置后面创建并返回一个新记录
	 * */
	public T insertAfter(int pos){
		T result=createRow();
		add(++pos,result);
		next();
		return result;
	}

	/**
	 * @param position 在指定位置前面创建并返回一个新记录
	 * */
	public T insert(int position){
		T result=createRow();
		add(position,result);
		next();
		return result;
	}

	/**
	 * @param row 注意：这个有指定插入位置的，不判断是否已有该实例，考虑的是超过1w条记录的插入性能
	 * @param position -1为尾部，学python的方式
	 * */
  @Override public ABRowSet<T> add(int position,@NonNull T row){
		if(position<=-1)
			position=rowList.size();
		rowList.add(position,row);//注意:多个对象共用一个IRow
		updateUniqueIndex(row,position);
    return this;
  }

	/**根据数据集的数据量进行自动判断合理的插入或替换记录，以确保主键唯一，注意主键不能为空*/
	public ABRowSet<T> replace(@NonNull T pkIdRow){
		if(pkIdRow.get(getPrimaryKeyName())==null)
			throw new SdkRuntimeException("主键'"+getPrimaryKeyName()+"'不能为空！");
		if(this.rowList.size()<100)
			return this.add(pkIdRow);
		else if(rowList.size()<1000){
			for(T existRow:rowList){
				Object key1=existRow.get(getPrimaryKeyName());
				Object key2=pkIdRow.get(getPrimaryKeyName());
				if(existRow==pkIdRow || key2.equals(key1)){
					return this;
				}
			}
			this.addLast(pkIdRow);
		}else{
			T existRow=findById(pkIdRow.get(getPrimaryKeyName()));
			if(existRow==null){
				return addLast(pkIdRow);
			}else
				return this;
		}
		return this;
	}

	/**
	 * 建议使用replace(T)代替add(T)，以保证主键和实例的唯一，又保证性能不受影响，并保持代码简洁
	 * @param row 注意：这个add单条记录会排除已有的row实例，避免重复实例
	 * */
  @Override public ABRowSet<T> add(@NonNull T row){
		if(!rowList.contains(row)){
			rowList.add(row);//注意:多个对象共用相同的IRow实例，要手工控制row修改的版本问题
			checkSize();
			updateUniqueIndex(row,rowList.size()-1);
		}
		return this;
  }

	/**从性能考虑，不判断是否已存在该记录，也为了省点事，不用传-1*/
	public ABRowSet<T> addLast(@NonNull T row){
		return add(this.rowList.size(),row);
	}

  @Override public ABRowSet<T> addAll(List<T> rows){
    rowList.addAll(rows);//注意:多个对象共用一个IRow
    checkSize();
		updateUniqueIndex(rows);
    return this;
  }

	public ABRowSet<T> addAll(@NonNull ABResultSet<T> rs){
		for(Iterator iter=rs.iterator();iter.hasNext();){
			Object obj=iter.next();
			if(obj instanceof IRow)
				rowList.add((T)obj);
			else if(obj instanceof Map)
				rowList.add((T)new ABRow((Map)obj));
			else
				throw new SQLRuntimeException("无法识别的类型:"+obj.getClass().toString());
		}
		return this;
	}

  @Override public ABRowSet<T> addAll(IReadRowSet rows){
    rowList.addAll(rows.getRows());//注意:多个对象共用一个IRow
    checkSize();
		updateUniqueIndex(rows.getRows());
    return this;
  }

	/**
	 * @param removeDuplicatedPkId 是否排除重复的记录，为true时，即返回两个数据集的并集
	 * */
	public ABRowSet<T> addAll(IReadRowSet<T> rows,boolean removeDuplicatedPkId){
		if(removeDuplicatedPkId){
			for(int i=0, n=rows.size();i<n;i++){
				T row=rows.getRow(i);
				if(this.indexOf(row)<0){
					add(row);
				}
			}
			checkSize();
			return this;
		}
		else
			return addAll(rows);
	}

	/**使用lambda表达式过滤，把找到的记录移除并返回
	 * 注意：lambda的性能较for循环差很多，数据集超过200条不建议使用
	 * */
	public ABRowSet<T> remove(Predicate<T> fn){
		ABRowSet<T> result=filter(fn);
		remove(result);
		recreateUniqueIndex();
		return this;
	}

  @Override public ABRowSet<T> remove(IRow row){
		if(row!=null){
			if(rowList.remove(row)){
				recreateUniqueIndex();
			}
		}
		return this;
  }

  @Override public ABRowSet<T> remove(IReadRowSet rs){
    return remove(rs.getRows());
  }

  @Override public ABRowSet<T> remove(Collection<T> rows){
  	boolean result=true;
  	for(IRow row:rows) {
			if(rowList.removeAll(rows)){
				recreateUniqueIndex();
			}
  	}
  	return this;
  }

  @Override public T remove(int index){
    checkRange(index);
   	T removedRow=(T)rowList.remove(index);
	 	if(removedRow!=null)
			 recreateUniqueIndex();
	 	return removedRow;
  }

	@Override  public ABRowSet<T> removeAll(){
		rowList.clear();
		recreateUniqueIndex();
		return this;
	}

  public void clear() {
  	removeAll();
  }

	public ABRowSet<T> distinct(String...fields){
		return (ABRowSet)RowSetUtils.distinct(this,fields);
	}

	/**把重复的记录删除。注意，返回的是已删除的记录集*/
	public ABRowSet<T> removeRepeat(String...fields){
		return (ABRowSet)RowSetUtils.removeRepeat(this,fields);
	}

	/**left join操作，类似于select的left join处理逻辑，适合处理1对1或1对N
	 * appendColumns：增加字段如果为空则获取记录集的所有字段。注意会排除重复字段，并且重复字段以原数据集为准，不会被新数据集更新。
	 * onFieldsMap：字段匹配条件，只能支持字段值匹配，不能指定常量值匹配，如有常量值条件应先执行过滤条件处理。
	 * 							key为原记录集的字段名，value为join记录集的字段名。
	 *              为null时会以两个记录集的重叠字段（去掉公用字段和主键）做为匹配条件。
	 * */
	public ABRowSet<T> join(IRowSet rs,String[] appendColumns,Map<String,String> onFieldsMap){
		if(rs==null || rs.size()==0)
			return this;
		if(esdk.obj.isEmpty(appendColumns)) {
			appendColumns=rs.getColumns();
		}
		appendColumns=esdk.str.remove(appendColumns,this.getColumns());
		for(String column:appendColumns) {
			addColumn(column);
		}
		if(onFieldsMap==null || onFieldsMap.keySet().size()==0) {
			String[] joinFields=esdk.array.overlap(this.getColumns(),rs.getColumns());
			joinFields=esdk.str.remove(esdk.str.remove(joinFields,Constant.SystemFields),"id");
			onFieldsMap=new LinkedHashMap();
			for(String field:joinFields) {
				onFieldsMap.put(field,field);
			}
		}
		for(IRow row:this) {
			RowExpressions exps=new RowExpressions();
			for(Entry<String,String> entry:onFieldsMap.entrySet()) {
				exps.add(RowExpression.create(null,entry.getValue(),row.get(entry.getKey())));
			}
			IRow findRow=rs.filter(1,exps).getFirstRow();
			if(findRow!=null)
				row.load(findRow.toMap(false,appendColumns),false,false,true);
		}
		return this;
	}

	/**
	 * @param appendColumns:多个增加的字段用逗号分隔。
	 * @param appendColumns:多个on字段用逗号分隔，支持相同的字段名，也可以支持用冒号分隔的字段名匹配（eg.: userName,userId:createUserId）
	 * */
	public ABRowSet<T> join(IRowSet rs,String appendColumns,String onColumns){
		String[] onColumnsArray=onColumns.split(",|;");
		String[] appendColumnsArray=esdk.str.split(appendColumns,",|;");
		LinkedHashMap<String,String> onFieldsMap=new LinkedHashMap<>(onColumnsArray.length);
		for(String column:onColumnsArray){
			if(!column.contains(":")) {
				onFieldsMap.put(column,column);
			}else {
				String[] keyVal=column.split(":");
				onFieldsMap.put(keyVal[0],keyVal[1]);
			}
		}
		return join(rs,appendColumnsArray,onFieldsMap);
	}

	public ABRowSet<T> join(IRowSet rs,String[] appendColumns,String... onColumns){
		LinkedHashMap<String,String> onFieldsMap=new LinkedHashMap<>(onColumns.length);
		for(String column:onColumns){
			onFieldsMap.put(column,column);
		}
		return join(rs,appendColumns,onFieldsMap);
	}

	@Override
	public ABRowSet<T> clone(){
		ABRowSet<T> result=new ABRowSet<T>();
		result.columns=this.columns;
		result.rowList=new ArrayList(this.size());
		result.rowList.addAll(this.rowList);
		result._rowCls=this._rowCls;
		return result;
	}

	public ABRowSet<T> clone(boolean deepClone){
		ABRowSet<T> result=new ABRowSet<T>();
		result.columns=this.columns;
		result.rowList=new ArrayList(this.size());
		if(deepClone){
			for(IRow item:this.rowList){
				result.rowList.add((T)item.clone());
			}
		}else
			result.rowList.addAll(this.rowList);
		result._rowCls=this._rowCls;
		return result;
	}

	@Override  public T setRow(int index,T row){
    checkRange(index);
    return (T)rowList.set(index,row);
  }

	@Override public ABRowSet<T> setColumns(String[] columns){
    if(!EasyObj.isEmpty(columns))
      this.columns=columns;
    return this;
  }

	public ARowSet<T> addColumn(@NonNull String name) {
		if(!EasyObj.isBlank(name)) {
			List<String> list=esdk.array.toList(columns);
			list.add(name);
			columns=list.toArray(new String[0]);
		}
		return this;
	}

	/**增加新多个字段*/
	public ARowSet<T> addColumns(String... names) {
		if(!EasyObj.isEmpty(names)) {
			List<String> list=esdk.array.toList(columns);
			for(String item:names) {
				list.add(item);
			}
			columns=list.toArray(new String[0]);
		}
		return this;
	}

	/**删除多个字段*/
	public ARowSet<T> removeColumns(String... names) {
		columns=esdk.array.remove(columns,names);
		return this;
	}

  @Override public ABRowSet<T> setColumns(@NonNull Collection names){
    if(names!=null)
      columns=Arrays.asList(names).toArray(new String[0]);
    return this;
  }


	public <R extends IRow<R>> @NonNull ABRowSet<R> map(@NonNull Func<T,R> func){
		try{
			ABRowSet<R> rs=new ABRowSet<>(rowList.size());
			for(T row: rowList){
				rs.add(func.invoke(row));
			}
			return rs;
		}catch(Exception e){
			throw esdk.tool.wrapThrowble(e);
		}
	}

}
